## -*- coding: utf-8 -*-

'''
Created on Jun 13, 2010

@author: pntruongan
'''
import Doc
import math
import random

import os
import wx

def Distance(A, B):
    #caculate the distance between two vector
    if len(A) != len(B):
        raise Exception("Dimension mismatched")
    #'''
    #Ecludiean distance
    sum_square = 0
    for i in range (len (A)):
        c = A[i] - B[i]
        sum_square += c*c
    
    return math.sqrt(sum_square)
    #'''
    '''
    #Mahatan distance
    sum = 0
    for i in range (len (A)):
        c = abs( A[i] - B[i])
        sum += c
    return sum
    #''
    #hamming distance
    sum = 0
    for i in range (len (A)):
        if A[i] != B[i]:
            sum += 1
    return sum
    '''

class Group:
    def __init__(self, meanVector = None):
        self.MeanVector = meanVector
        self.DocList = []
    
    def recalculateMeanVector(self):
        '''
        Update the MeanVector according to the Documents in DocList
        '''
        for i in range(len(self.MeanVector)):
            sum_co = 0
            for j in range(len(self.DocList)):
                sum_co += self.DocList[j].Vector[i]
            self.MeanVector[i] = sum_co*1.0 / len(self.DocList)
class KMeans:
    def __init__(self, k, n, docSet):
        '''
        k is the number of group/cluster a must have parameter 
        n is the number of loop time before the algorithm stop
        docset is a DocumetnCollection on which the alogrithm run
        '''
        self.K = k
        self.N = n
 
        #self.DocSet = Doc.DocumentCollection()
        self.DocSet = docSet
        
        #Those following variable use to control the loop and report progress
        self.__progress = 0
        self.__looptime = 0
        
        
        #This is the result of the algorithm
        self.GroupList = []
        pass
    
    def Progress(self):
        '''
        report the progress in percent 
        '''
        pass
    
    
    def __preProcess(self):
        '''
        Prepare the k empty group and their random mean vector
        '''
        random.seed(6)
        rand = []
        for i in range(self.K):
            #Lay random 1 vector van ban nao do lam mean vector cua nhom
            k = random.randint(0, self.DocSet.getDocListLen()-1)
            while k in rand: #Neu k trung voi mot vector da lay thi chon lai
                k = random.randint(0, self.DocSet.getDocListLen()-1 )
            rand.append(k)
            
            group = Group(self.DocSet.getDocumentVector(k))
            #group.MeanVector = self.DocSet.getDocumentVector(k)[:]
            self.GroupList.append(group)
            '''
            temp = []
            #The mean vector is generate by divide the maximum appear time of ea
            #word in documents into K range.
            for j in range(len(self.DocSet.GlobalVocabulary[1])):
                #temp.append( self.DocSet.GlobalVocabulary[1][j] / self.K * i )
                temp.append( random.randint(0, self.DocSet.GlobalVocabulary[1][j]))
            group = Group(temp)
            group.MeanVector = temp
            self.GroupList.append(group)
            '''
        pass
    
    def Start(self, gauge = None):
        '''
        Xác định k nhóm,
        2) Mỗi nhóm được dại diện bằng một vector trọng tâm - Khỏi tọa ngayu nhien k vector trọgn tâm 
        3) Vòng lặp 
            a. Lây random 1 vector input, tính distance từ vector đó dến k vêtor trọng tâm
            b. Xác định nhóm có vector trọng tam gần với input nhất. Đặc v
            c. Cập nhật vêtor trong tam ( quá trịnh học ) --> lay avg vector của nhóm các vector
        gauge is progressBar if it is not None, this procedure will try to report its progress
        via this progressbar 
        '''
        #Xac dinh k nhom 
        self.__preProcess()
        # Generate random mean vector for each group
        
        for self.__looptime in range(self.N):
            for i in self.GroupList:
                i.DocList = []
            for i in range(self.DocSet.getDocListLen()):
                closestGroup = self.ProcessVector(self.DocSet.getDocumentVector(i))
                
                doc = Doc.Document(self.DocSet.getDocumentName(i))
                doc.Vector = self.DocSet.getDocumentVector(i)
                
                self.GroupList[closestGroup].DocList.append(doc)
            
            backupMeanVector = []
            for g in self.GroupList:
                #print("g mean vector ",g.MeanVector)
                backupMeanVector.append(g.MeanVector[:])
                g.recalculateMeanVector()
            
            #recheck the updated meanvector
            #if they are the same with mean vector, we have the rights to break.
            br = True
            for i in range(len(self.GroupList)):
                #print("Group " + str(i), Distance( self.GroupList[i].MeanVector , backupMeanVector[i]))
                #print("new", self.GroupList[i].MeanVector )
                #print("old", backupMeanVector[i], "\n")
                if Distance( self.GroupList[i].MeanVector , backupMeanVector[i]) != 0:
                    br = False
            
            if br:
                if gauge is not None:
                    wx.CallAfter(gauge.SetValue, 100)
                break
            
            if gauge is not None:
                a = (self.__looptime + 1) * 100 / self.N
                print a, '%'
                wx.CallAfter(gauge.SetValue, (self.__looptime + 1) * 100 / self.N)
            
            #'''
            print "######## K-Means" , self.__looptime + 1, "#################"
            for i in self.GroupList:
                print "---Group---", self.GroupList.index(i )
                b = []
                c = []
                
                for j in i.DocList:
                    f = os.path.basename(j.Name)
                    try:
                        f1 = f.split('.')[1]
                    except IndexError:
                        print f 
                    #f2 = f1[0] + f1[1]
                    if f1 not in b:
                        b.append(f1)
                        c.append(1)
                    else:
                        i = b.index(f1)
                        c[i] += 1
                print len(b)
                for i in range(len(b)):
                    print b[i], c[i]
            
        print ' K-MeansDone'
        #'''
        
    def ProcessVector(self, vector):
        '''
        Process a single document. Return the group Index that document should be in
        Do not change any global value here, just do the calculation and processing
        '''    
        min_distance = Distance(vector, self.GroupList[0].MeanVector)
        index = 0
        for i in range(1,len(self.GroupList)):
            d = Distance(vector, self.GroupList[i].MeanVector)
            if min_distance > d:
                min_distance = d
                index = i
        return index
    
    def TestDoc(self, doc):
        '''
        This method is used to test the result after the algorith has finish
        It takes a single document (not necessarily from the DocumentCollection)
        and determine which group should that document be catogrized
        
        doc is a string
        '''
        #get the new doc's vector
        test = self.DocSet.getTestDocVector(doc)
